// AUTHOR   : Based on article from Fons Sonnemans (Reflection it)
//
// DETAILS  :  The DualList component handles the movement of items between 2 listboxes.
//
using System;
using System.ComponentModel;
using System.Collections;
using System.Diagnostics;
using System.Windows.Forms;
using System.Drawing;

namespace SdmxMl.UC
{
	/// <summary>
	/// DualList Action enum
	/// </summary>
	public enum DualListAction 
	{
		/// Move the selected item.
		MoveSelected = 0,
		/// Copy the selected item.
		CopySelected = 1,
		/// Move all items.
		MoveAll = 2,
		/// Copy all items.
		CopyAll = 3,
	}

	/// <summary>
	/// BeforeActionHandler
	/// </summary>
	public delegate void BeforeActionHandler(object sender, DualListActionCancelEventArgs e);

	/// <summary>
	/// AfterActionHandler
	/// </summary>
	public delegate void AfterActionHandler(object sender, DualListActionEventArgs e);


	/// <summary>
	/// The DualList component handles the movement of items between 2 listboxes.
	/// </summary>
	/// <remarks>
	/// The DualList component takes care of the following features:
	/// - Actions: Move Selected, Copy Selected, Move All, Copy All
	/// - DoubleClick support, inclusive (re)setting the Default button
	/// - Select next item
	/// - Enable and disable buttons
	/// - MultiSelect support
	/// You can place it on the form as a sort of "invisible control". 
	/// Next you set its behaviour properties: Action, Button, ListBoxFrom and ListBoxTo. 
	/// Optionally you can set the DoubleClickSupport and AutoDisableButton properties. 
	/// </remarks>
	[
	// Set Projects Default Namespace to current NameSpace. Add 16x16x16 bitmap to the project, set its BuildAction to: Embedded Resource
	ToolboxBitmap(typeof(Bitmap), "Ctrl.DualList.DualList.bmp"),
	DefaultProperty("Button"),
	DefaultEvent("BeforeAction"),
	Designer(typeof(SdmxMl.UC.DualListDesigner))
	]
	public class DualList : System.ComponentModel.Component
	{
		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;

		/// <summary>
		/// Construct a DualList object giving the container.
		/// </summary>
		/// <param name="container">the container</param>
		public DualList(System.ComponentModel.IContainer container)
		{
			//
			// Required for Windows.Forms Class Composition Designer support
			//
			container.Add(this);
			InitializeComponent();

			//
			// TODO: Add any constructor code after InitializeComponent call
			//
		}

		/// <summary>
		/// Construct a DualList object.
		/// </summary>
		public DualList()
		{
			//
			// Required for Windows.Forms Class Composition Designer support
			//
			InitializeComponent();

			//
			// TODO: Add any constructor code after InitializeComponent call
			//
		}

		#region Component Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			components = new System.ComponentModel.Container();
		}
		#endregion

		/// <summary>
		/// Before event
		/// </summary>
		[
		Category("Action"),
		Description("Before event"),
		]
		public event BeforeActionHandler BeforeAction;

		/// <summary>
		/// After event
		/// </summary>
		[
		Category("Action"),
		Description("After event"),
		]
		public event AfterActionHandler AfterAction;

		// Controls
		private ListBox mListBoxFrom;
		private ListBox mListBoxTo;
		private Button mButton;

		// Properties
		private bool mDoubleClickSupport = false;
		private DualListAction mAction = DualListAction.MoveSelected;
		private bool mAutoDisableButton = false;
		private bool mBusy = false;

		// helpers
		private IButtonControl mPreviousAcceptButton;

		/// <summary>
		/// Button Click EventHandler
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected virtual void ButtonClick(object sender, System.EventArgs e)
		{
			Click();
		}

		/// <summary>
		/// ListBoxFrom Leave EventHandler
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected virtual void FromEnter(object sender, System.EventArgs e)
		{
			if (DoubleClickSupport)
			{
				// Set the AcceptButton of the Form
				Form f = mListBoxFrom.FindForm();
				mPreviousAcceptButton = f.AcceptButton;
				f.AcceptButton = mButton;
			}
		}

		/// <summary>
		/// ListBoxFrom Leave EventHandler
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected virtual void FromLeave(object sender, System.EventArgs e)
		{
			if (DoubleClickSupport)
			{
				// Reset the AcceptButton of the Form
				Form f = mListBoxFrom.FindForm();
				f.AcceptButton = mPreviousAcceptButton;
			}
		}

		/// <summary>
		/// ListBoxFrom DoubleClick EventHandler
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected virtual void FromDoubleClick(object sender, System.EventArgs e)
		{
			if (DoubleClickSupport)
			{
				Click();
			}
		}

		/// <summary>
		/// ListBoxFrom SelectedIndexChanged EventHandler, Enables/Disables the button
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected virtual void SelectedIndexChanged(object sender, System.EventArgs e)
		{
			EnableDisable();
		}

		/// <summary>
		/// Returns/sets the ListBox where the items are moved/copied from
		/// </summary>
		[
		Category("Behavior"),
		Description("ListBox which the item(s) are moved/copied from"),
		Bindable(true),
		]
		public virtual ListBox ListBoxFrom
		{
			get { return mListBoxFrom; }
			set 
			{ 
				mListBoxFrom = value;
				if (mListBoxFrom != null)
				{
					mListBoxFrom.Enter += new System.EventHandler(this.FromEnter);
					mListBoxFrom.Leave += new System.EventHandler(this.FromLeave);
					mListBoxFrom.DoubleClick += new System.EventHandler(this.FromDoubleClick);
					mListBoxFrom.SelectedIndexChanged += new System.EventHandler(this.SelectedIndexChanged );
				}
				EnableDisable();
			}
		}

		/// <summary>
		/// Returns/sets the ListBox where the items are moved/copied to
		/// </summary>
		[
		Category("Behavior"),
		Description("ListBox where the item(s) are moved/copied to"),
		Bindable(true),
		]
		public virtual ListBox ListBoxTo
		{
			get { return mListBoxTo; }
			set { mListBoxTo = value; }
		}

		/// <summary>
		/// Returns/sets the Button which Click event is handled
		/// </summary>
		[
		Category("Behavior"),
		Description("Button that activates the Action"),
		Bindable(true),
		]
		public virtual Button Button
		{
			get { return mButton; }
			set
			{
				mButton = value;
				if (mButton != null)
				{
					mButton.Click += new System.EventHandler(this.ButtonClick);
					EnableDisable();
				}
			}
		}

		/// <summary>
		/// Returns/sets the DoubleClickSupport
		/// </summary>
		[
		Category("Behavior"),
		Description("Returns/sets the DoubleClickSupport"),
		DefaultValue(false),
		Bindable(true),
		]
		public virtual bool DoubleClickSupport
		{
			get { return mDoubleClickSupport; }
			set { mDoubleClickSupport = value; }
		}

		/// <summary>
		/// Returns/sets the DoubleClickSupport
		/// </summary>
		[
		Category("Behavior"),
		Description("Returns/sets the DoubleClickSupport"),
		DefaultValue(false),
		Bindable(true),
		]
		public virtual bool AutoDisableButton
		{
			get { return mAutoDisableButton; }
			set 
			{ 
				mAutoDisableButton = value; 
				if (value)
					EnableDisable();
				else
					Button.Enabled = true;
			}
		}

		/// <summary>
		/// Returns/sets the Action which is executed when the Button is clicked
		/// </summary>
		[
		Category("Behavior"),
		Description("Returns/sets the Action which is executed when the Button is clicked"),
		DefaultValue(DualListAction.MoveSelected),
		Bindable(true),
		]
		public virtual DualListAction Action
		{
			get { return mAction; }
			set { mAction = value; }
		}

		/// <summary>
		/// Move the selected Item from ListBoxFrom to ListBoxTo
		/// </summary>
		public virtual void MoveSelected()
		{
			mBusy = true;
			DoSelected(DualListAction.MoveSelected);
			mBusy = false;
			EnableDisable();
		}

		/// <summary>
		/// Copy the selected Item from ListBoxFrom to ListBoxTo
		/// </summary>
		public virtual void CopySelected()
		{
			mBusy = true;
			DoSelected(DualListAction.CopySelected);
			mBusy = false;
			EnableDisable();
		}

		/// <summary>
		/// Move all Items from ListBoxFrom to ListBoxTo
		/// </summary>
		public virtual void MoveAll() 
		{
			mBusy = true;
			for (int t = 0; t < mListBoxFrom.Items.Count; t++)
			{
				DoAction(DualListAction.MoveAll, false, t);
			}
			mListBoxFrom.Items.Clear();
			mBusy = false;
			EnableDisable();
		}

		/// <summary>
		/// Copy all Items from ListBoxFrom to ListBoxTo
		/// </summary>
		public virtual void CopyAll()
		{
			mBusy = true;
			for (int t = 0; t < mListBoxFrom.Items.Count; t++)
			{
				DoAction(DualListAction.CopyAll, false, t);
			}
			mBusy = false;
			EnableDisable();
		}	

		/// <summary>
		/// Decide what to do
		/// </summary>
		protected virtual void Click()
		{
			if (mButton != null & ListBoxFrom != null & ListBoxTo != null)
			{
				switch (Action)
				{
					case DualListAction.MoveSelected:
						MoveSelected();
						break;
					case DualListAction.MoveAll:
						MoveAll();
						break;
					case DualListAction.CopySelected:
						CopySelected();
						break;
					case DualListAction.CopyAll:
						CopyAll();
						break;
				}
			}
		}

		/// <summary>
		/// Move or Copy the selected Item(s)
		/// </summary>
		/// <param name="action">the DualList action</param>
		protected virtual void DoSelected(DualListAction action)
		{
			if (mListBoxFrom.SelectionMode == SelectionMode.One)
			{
				// Single Select
				if (mListBoxFrom.SelectedIndex > -1)
				{
					int i = mListBoxFrom.SelectedIndex;
					if (action == DualListAction.MoveSelected)
					{
						DoAction(action, true, i);
					} 
					else
					{
						DoAction(action, false, i);
						i += 1;
					}
                
					// select next/previous item
					if (i >= (mListBoxFrom.Items.Count))
						i = mListBoxFrom.Items.Count -1;
					mListBoxFrom.SelectedIndex = i;
				}
			}
			else
			{
				// MultiSelect

				// Add all selected items
				foreach (int x in mListBoxFrom.SelectedIndices)
				{
					DoAction(action, false, x);
				}

				// Remove or de-select all selected items
				int i;
				if ((mListBoxFrom.SelectedIndices.Count) > 0) 
					i = mListBoxFrom.SelectedIndices[mListBoxFrom.SelectedIndices.Count - 1] + 1;
				else
					return;

				for (int t = mListBoxFrom.Items.Count - 1; t >= 0; t--)
				{
					if (action == DualListAction.MoveSelected)
					{
						if (mListBoxFrom.SelectedIndices.Contains(t))
						{
							i = t;
							mListBoxFrom.Items.RemoveAt(t);
						}
					} 
					else
					{
						// de-select
						mListBoxFrom.SetSelected(t, false);
					}
				}

				// select next/previous item
				if (i >= (mListBoxFrom.Items.Count))
					i = mListBoxFrom.Items.Count -1;
				if (i > -1)
					mListBoxFrom.SetSelected(i, true);

			}
		}

		/// <summary>
		/// Enable/Disable button
		/// </summary>
		protected virtual void EnableDisable()
		{
			if (AutoDisableButton & !mBusy & mButton != null & ListBoxFrom != null)
			{
				if ((Action == DualListAction.MoveSelected) | (Action == DualListAction.CopySelected))
				{
					if (Button.Enabled != (ListBoxFrom.SelectedIndex > -1))
						Button.Enabled = (ListBoxFrom.SelectedIndex > -1);
				}
				else
				{
					if (Button.Enabled != (ListBoxFrom.Items.Count > 0))
						Button.Enabled = (ListBoxFrom.Items.Count > 0);
				}
			}
		}

		/// <summary>
		/// Move/Copy the given Item and raise the Before and After events.
		/// </summary>
		/// <param name="action">Action</param>
		/// <param name="remove">True is Remove item</param>
		/// <param name="index">Index of Item to Move/Copy</param>
		protected virtual void DoAction(DualListAction action, bool remove, int index)
		{
			DualListActionCancelEventArgs e = new DualListActionCancelEventArgs(DualListAction.MoveAll , mListBoxFrom.Items[index]);

			// Raise Before event
			if (BeforeAction != null)
			{
				BeforeAction(this, e);
			}

			// If not canceled
			if (!e.Cancel)
			{
				// Add the item
				int newIndex = mListBoxTo.Items.Add(e.Item);

				// Remove the item
				if (remove)
					mListBoxFrom.Items.RemoveAt(index);

				// Raise After event
				if (AfterAction != null)
				{
					DualListActionEventArgs e2 = new DualListActionEventArgs(DualListAction.MoveAll , e.Item, newIndex);
					AfterAction(this, e2);
				}

			}
		}

	}
}
